//
// GPSMapEdit
// (c) Konstantin Galichsky (kg@geopainting.com), 2003-2007
// ALAN and ALAN Map 500 are registered trademarks of ALAN Group.
//
// ALAN Map 500 (OS 3.x) TRL track parser.
//

# include "StdAfx.h"
# include "AlanTrl3.h"
# include "Map.h"
# include "Types.h"
# include "Load.h"
# include "Log.h"
# include "Status.h"
# include "Distance.h"
# include "ParseHelpers.h"
# include "fast_floor.h"
# include "Wrappers.h"
# include "Globals.h"

class CAlanTrl3CFLoader : public CMapLoader {
  public:
	// Override.
	virtual bool IsValid (const char * _pData, size_t _cFileSize) const;
	virtual LoadStatus_t Load (const char * _pData, size_t _cFileSize, const char * _strFileName, map_t * _pMap);
};

DECLARE_WPTRK_LOADER (CAlanTrl3CFLoader, "alan-trl-3cf", "trl", "ALAN Map 500 OS 3.x CF tracks (*.trl)", ftiTracks);

bool CAlanTrl3CFLoader::IsValid (const char * _pData, size_t _cFileSize) const {
	if (_cFileSize < c_cTrl3CFHdrOffset + sizeof (CTrl3CFHdr))
		return false;

	const DWORD dwSignature = reinterpret_cast<const CTrl3CFHdr *> (_pData + c_cTrl3CFHdrOffset)->dwSignature;
	if (dwSignature == c_dwTrl3Signature || dwSignature == c_dwTrl3Signature0)
		return true;

	return false;
}

LoadStatus_t CAlanTrl3CFLoader::Load (const char * _pData, size_t _cFileSize, const char * _strFileName, map_t * _pMap) {
	const char * const pEnd = _pData + _cFileSize;

	file_refs_t file_refs;
	file_refs.push_back (file_ref_t ());
	file_refs.back ().strFileName = _strFileName;
	file_refs.back ().bAttachment = true;

	SetProgressStatus (0);

	// Get time zone shift.
	TIME_ZONE_INFORMATION tzi;
	if (::GetTimeZoneInformation (& tzi) == TIME_ZONE_ID_DAYLIGHT)
		tzi.Bias += tzi.DaylightBias;
	const double dtTimeShift = float (tzi.Bias)/(24*60);

	const CTrl3CFHdr      * const pHdr  = reinterpret_cast<const CTrl3CFHdr *> (_pData + c_cTrl3CFHdrOffset);
	const CTrl3CFTrackLog * const pLogs = reinterpret_cast<const CTrl3CFTrackLog *> (_pData);

	// TODO: research.
	const int nDay    =   pHdr->dwDate        & 0xFF;
	const int nMonth  = ((pHdr->dwDate >> 8)  & 0xF);
	const int nYear   = ((pHdr->dwDate >> 12) & 0xF) + 1990;
	const int nSecond =  pHdr->dwTime        & 0x3F;
	const int nMinute = (pHdr->dwTime >> 8)  & 0x3F;
	const int nHour   = (pHdr->dwTime >> 14) & 0x1F;
//	ReportText ("* Date: %02d-%02d-%04d", nDay, nMonth, nYear);
//	ReportText ("* Time: %02d:%02d:%02d", nHour, nMinute, nSecond);

	for (size_t cTrack = 0; /*cTrack < pHdr->wTracks &&*/ cTrack < c_cTrl3CFMaxTracks; ++ cTrack) {
		const CTrl3Track & track = pHdr->tracks [cTrack];
		if (track.dwOccupied == 0)
			continue;
		if (track.wPoints > c_cTrl3MaxPoints)
			continue;

		const CTrl3CFTrackLog & log = pLogs [cTrack];

		//
		// Create track.
		//
		rgn_t & rgn = _pMap->CreateRgn (rkPolyline);

		rgn.SetTrackType ();

		rgn.pFileRef  = & file_refs.back ();
		rgn.cBeginPos = reinterpret_cast<const char *> (& log) - _pData;
		rgn.cEndPos   = pEnd - _pData;
		rgn.cFileLine = -1;

		rgn.strLabel.assign   (track.strName);
		rgn.strComment.assign (track.strComment, GetLineLen (track.strComment, track.strComment + sizeof (track.strComment) - 1));

		rgn.elements.push_back (rgn_element_t ());
		rgn_element_t & element = rgn.elements.back ();
		element.cLevel      = 0;
		element.cFileOffset = rgn.cBeginPos;

		// Extra.
		track_info_t * const pExtra = rgn.pExtra->wtr.CreateTrack ();

		if (track.dwShow == 0)
			pExtra->wFlags |= track_info_t::defHideInGPS;

		points_t & points = element.points;
		points.resize (track.wPoints);
		element.pExtra->TrackPoints.resize (track.wPoints);
		for (size_t cPoint = 0; cPoint < track.wPoints && cPoint < c_cTrl3MaxPoints; ++ cPoint) {
			const CTrl3Point & pt = log.points [cPoint];
			point_t & point = points [cPoint];

			// Position.
			point.x =  pt.lX/36000.f;
			point.y = -pt.lY/36000.f;

			const CTrl3Extra & extra = log.extra [cPoint];
			const CTrl3Time  & time  = log.time  [cPoint];

			// NOTE: the 0-th point contains incorrect altitude and speed.
			trackpoint_info_t & tpi = element.pExtra->TrackPoints [cPoint];

			// Altitude.
			tpi.wFlags |= trackpoint_info_t::defAltitude;
			tpi.fAltitude = extra.wAltitude*c_fTrl3CFAltitudeFactor;

			// Speed.
			tpi.wFlags |= trackpoint_info_t::defSpeed;
			tpi.fSpeed = extra.wSpeed*c_fTrl3CFSpeedFactor;

			// DOP.
			tpi.wFlags |= trackpoint_info_t::defDOP;
			tpi.fDOP = time.dop*c_fTrlDOPFactor;

			// Time.
			SYSTEMTIME st;
			st.wYear   = track.btYear + 2000 - 208;
			st.wMonth  = track.btMonth;
			st.wDay    = track.btDay;
			st.wHour   = time.hour;
			st.wMinute = time.min;
			st.wSecond = time.sec;
			st.wMilliseconds = 0;
			DATE dt;
			::SystemTimeToVariantTime (& st, & dt);

			tpi.wFlags |= trackpoint_info_t::defTime;
			tpi.dtTime = dt + dtTimeShift;
		}

		SetProgressStatus (50*cTrack/c_cTrl3CFMaxTracks);
	}

	_pMap->file_refs.swap (file_refs);

	// Suppress warning about empty Zoom etc.
	_pMap->bNoHeader = true;
	_pMap->cHeaderLen = 0;

	SetProgressStatus (50);

	return lsOK;
}

///////////////////////////////////////////////////////////////////////////////////

class CAlanTrl3FLLoader : public CMapLoader {
  public:
	// Override.
	virtual bool IsValid (const char * _pData, size_t _cFileSize) const;
	virtual LoadStatus_t Load (const char * _pData, size_t _cFileSize, const char * _strFileName, map_t * _pMap);
};

//DECLARE_WPTRK_LOADER (CAlanTrl3FLLoader, "alan-trl-3fl", "trl", "ALAN Map 500 OS 3.x FL tracks (*.trl)");

bool CAlanTrl3FLLoader::IsValid (const char * _pData, size_t _cFileSize) const {
	if (_cFileSize < c_cTrl3FLHdrOffset + sizeof (CTrl3FLHdr))
		return false;

	if (reinterpret_cast<const CTrl3FLHdr *> (_pData + c_cTrl3FLHdrOffset)->dwSignature == c_dwTrl3Signature)
		return true;

	return false;
}

LoadStatus_t CAlanTrl3FLLoader::Load (const char * _pData, size_t _cFileSize, const char * _strFileName, map_t * _pMap) {
	const char * const pEnd  = _pData + _cFileSize;

	file_refs_t file_refs;
	file_refs.push_back (file_ref_t ());
	file_refs.back ().strFileName = _strFileName;
	file_refs.back ().bAttachment = true;

	SetProgressStatus (0);

	const CTrl3FLHdr      * const pHdr = reinterpret_cast<const CTrl3FLHdr *> (_pData + c_cTrl3FLHdrOffset);
	const CTrl3FLTrackLog * const pLogs [c_cTrl3FLMaxTracks] = {
		reinterpret_cast<const CTrl3FLTrackLog *> (_pData),
		reinterpret_cast<const CTrl3FLTrackLog *> (_pData + sizeof (CTrl3FLTrackLog)),
		reinterpret_cast<const CTrl3FLTrackLog *> (_pData + 0x10000),
		reinterpret_cast<const CTrl3FLTrackLog *> (_pData + 0x10000 + sizeof (CTrl3FLTrackLog)),
		reinterpret_cast<const CTrl3FLTrackLog *> (_pData + 0x20000),
		reinterpret_cast<const CTrl3FLTrackLog *> (_pData + 0x20000 + sizeof (CTrl3FLTrackLog)),
		reinterpret_cast<const CTrl3FLTrackLog *> (_pData + 0x30000),
		reinterpret_cast<const CTrl3FLTrackLog *> (_pData + 0x30000 + sizeof (CTrl3FLTrackLog)),
	};

	// TODO: research.
	const int nDay    =   pHdr->dwDate        & 0xFF;
	const int nMonth  = ((pHdr->dwDate >> 8)  & 0xF);
	const int nYear   = ((pHdr->dwDate >> 12) & 0xF) + 1990;
	const int nSecond =  pHdr->dwTime        & 0x3F;
	const int nMinute = (pHdr->dwTime >> 8)  & 0x3F;
	const int nHour   = (pHdr->dwTime >> 14) & 0x1F;
//	ReportText ("* Date: %02d-%02d-%04d", nDay, nMonth, nYear);
//	ReportText ("* Time: %02d:%02d:%02d", nHour, nMinute, nSecond);

	for (size_t cTrack = 0; /*cTrack < pHdr->wTracks &&*/ cTrack < c_cTrl3FLMaxTracks; ++ cTrack) {
		const CTrl3Track & track = pHdr->tracks [cTrack];
		if (track.dwOccupied == 0)
			continue;
		if (track.wPoints > c_cTrl3MaxPoints)
			continue;

		const CTrl3FLTrackLog & log = * (pLogs [cTrack]);

		//
		// Create track.
		//
		rgn_t & rgn = _pMap->CreateRgn (rkPolyline);

		rgn.SetTrackType ();

		rgn.pFileRef  = & file_refs.back ();
		rgn.cBeginPos = reinterpret_cast<const char *> (& log) - _pData;
		rgn.cEndPos   = pEnd - _pData;
		rgn.cFileLine = -1;

		rgn.strLabel.assign   (track.strName);
		rgn.strComment.assign (track.strComment, GetLineLen (track.strComment, track.strComment + sizeof (track.strComment) - 1));

		rgn.elements.push_back (rgn_element_t ());
		rgn_element_t & element = rgn.elements.back ();
		element.cLevel      = 0;
		element.cFileOffset = rgn.cBeginPos;

		// Extra.
		track_info_t * const pExtra = rgn.pExtra->wtr.CreateTrack ();

		if (track.dwShow == 0)
			pExtra->wFlags |= track_info_t::defHideInGPS;

		points_t & points = element.points;
		points.resize (track.wPoints);
		element.pExtra->TrackPoints.resize (track.wPoints);
		for (size_t cPoint = 0; cPoint < track.wPoints && cPoint < c_cTrl3MaxPoints; ++ cPoint) {
			const CTrl3Point & pt = log.points [cPoint];
			point_t & point = points [cPoint];

			// Position.
			point.x =  pt.lX/36000.f;
			point.y = -pt.lY/36000.f;

			const CTrl3Extra & extra = log.extra [cPoint];

			if (cPoint > 0) {
				// NOTE: the 0-th point contains incorrect altitude and speed.
				trackpoint_info_t & tpi = element.pExtra->TrackPoints [cPoint];

				// Altitude.
				tpi.wFlags |= trackpoint_info_t::defAltitude;
				tpi.fAltitude = extra.wAltitude*c_fTrl3CFAltitudeFactor;

				// Speed.
				tpi.wFlags |= trackpoint_info_t::defSpeed;
				tpi.fSpeed = extra.wSpeed*c_fTrl3CFSpeedFactor;
			}
		}

		SetProgressStatus (50*cTrack/c_cTrl3FLMaxTracks);
	}

	_pMap->file_refs.swap (file_refs);

	// Suppress warning about empty Zoom etc.
	_pMap->bNoHeader = true;
	_pMap->cHeaderLen = 0;

	SetProgressStatus (50);

	return lsOK;
}

